<!DOCTYPE html>
<svg width="960" height="500">
    <path transform="translate(180,150)scale(2,2)" fill="none" stroke="black" stroke-width="1.5"></path>
</svg>
<script src="d3.min.js"></script>
<script>

    var d0 = "M0,0c100,0 0,100 100,100c100,0 0,-100 100,-100",
        d1 = "M0,0c100,0 0,100 100,100c100,0 0,-100 100,-100c100,0 0,100 100,100";

    d3.select("path")
        .attr("d", d0)
        .transition()
        .duration(2000)
        .on("start", function repeat() {
            d3.active(this)
                .attrTween("d", pathTween(d1, 4))
                .transition()
                .attrTween("d", pathTween(d0, 4))
                .transition()
                .on("start", repeat);
        });

    function pathTween(d1, precision) {
        return function () {
            var path0 = this,
                path1 = path0.cloneNode(),
                n0 = path0.getTotalLength(),
                n1 = (path1.setAttribute("d", d1), path1).getTotalLength();

            // Uniform sampling of distance based on specified precision.
            var distances = [0], i = 0, dt = precision / Math.max(n0, n1);
            while ((i += dt) < 1) distances.push(i);
            distances.push(1);

            // Compute point-interpolators at each distance.
            var points = distances.map(function (t) {
                var p0 = path0.getPointAtLength(t * n0),
                    p1 = path1.getPointAtLength(t * n1);
                return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]);
            });

            return function (t) {
                return t < 1 ? "M" + points.map(function (p) { return p(t); }).join("L") : d1;
            };
        };
    }

</script>